/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/

/**
 * @brief API implementation of class IR-codes emitter.
 *
 * @author  WXQ#XDU
 * @date    2025.01
 */

#include <stdlib.h>

#include "../include/amcc_error.h"
#include "../include/amcc_log.h"
#include "../include/ircode.h"

#include "inside/_emit_inner.h"

extern void  codemgr_module_init();   // in codemgr.c
extern void  codemgr_module_clean();
extern void  backpatch_module_init(); // in backpatch.c
extern void  backpatch_module_clean();

/** initializes internal data of emitter module */
int emitter_module_init() {

    // All attributes of syntactic structures are attached
    //    to parse tree nodes.
    // So the annotation de-allocator MUST be registered in tree module.
    //
    // See t_tree.pAnnotation in file tree.h.
    TREEFUNC_SET_free_annotation(codeattr_destroy);
    backpatch_module_init();
    codemgr_module_init();

    return 0;
}

/** cleans internal data of emitter module */
void emitter_module_clean() {
    codemgr_module_clean();
    backpatch_module_clean();
}



t_emitter_ptr ircode_emitter_new(t_tree_ptr     pParseTreeRoot,
                                 t_symtable_ptr pSymTableRoot )
{
    t_emitter_ptr pThis = (t_emitter_ptr)calloc(1, sizeof(*pThis));
    if (NULL == pThis) {
        ERROR_REPORT_MALLOC();
        return NULL;
    }

// Step 1:  initialize the basic object

    t_tree_listener * listenerPtr = (t_tree_listener_ptr)pThis;
    tree_listener_init0( listenerPtr, sizeof(*pThis) );

// Step 2:  initialize my members

    pThis->treeRootPtr      = pParseTreeRoot;
    pThis->symtableRootPtr  = pSymTableRoot;

    stack_init( &(pThis->functionStack), NULL );
    pThis->tempmgrPtr       = tempmgr_new( pThis );
    pThis->ircodemgrPtr     = codemgr_new( pThis );

// Step 3:  overrides super methods (semantic action) on demand

    listenerPtr->enter_translationUnit      = emit_enter_translationUnit;
    listenerPtr->exit_translationUnit       = emit_exit_translationUnit;
    listenerPtr->enter_funcDeclaration      = emit_enter_functionDeclaration;
    listenerPtr->exit_funcDeclaration       = emit_exit_functionDeclaration;
    listenerPtr->exit_parameterList         = emit_exit_parameterList;
    listenerPtr->exit_initDeclarator        = emit_exit_initDeclarator;

    listenerPtr->exit_expression            = emit_exit_expression;
    listenerPtr->exit_assignmentExpression  = emit_exit_assignmentExpr;
    listenerPtr->exit_logicalOrExpression   = emit_exit_logicalOrExpr;
    listenerPtr->exit_logicalAndExpression  = emit_exit_logicalAndExpr;
    listenerPtr->exit_equalityExpression    = emit_exit_equalityExpr;
    listenerPtr->exit_relationalExpression  = emit_exit_relationalExpr;
    listenerPtr->exit_additiveExpression    = emit_exit_additiveExpr;
    listenerPtr->exit_multiplicativeExpression = emit_exit_multiplicativeExpr;
    listenerPtr->exit_unaryExpression       = emit_exit_unaryExpr;
    listenerPtr->exit_primaryExpression     = emit_exit_primaryExpr;
    listenerPtr->exit_leftValue             = emit_exit_leftValue;
    listenerPtr->exit_functionCall          = emit_exit_functionCall;
    listenerPtr->exit_argumentList          = emit_exit_argumentList;

    listenerPtr->visitTerminalNode          = emit_visit_terminal;

    listenerPtr->enter_statement            = emit_enter_statement;
    listenerPtr->exit_statement             = emit_exit_statement;
    listenerPtr->exit_selectionStatement    = emit_exit_selectionStmt;
    listenerPtr->exit_iterationStatement    = emit_exit_iterationStmt;
    listenerPtr->enter_compoundStatement    = emit_enter_compoundStmt;
    listenerPtr->exit_compoundStatement     = emit_exit_compoundStmt;
    listenerPtr->enter_blockItem            = emit_enter_blockItemStmt;
    listenerPtr->exit_blockItem             = emit_exit_blockItemStmt;
    listenerPtr->exit_expressionStatement   = emit_exit_expressionStmt;
    listenerPtr->exit_jumpStatement         = emit_exit_jumpStmt;

    listenerPtr->enter_labeledStatement     = emit_enter_labeledStmt;
    listenerPtr->exit_labeledStatement      = emit_exit_labeledStmt;

    log_debug("Emitter %tx has created and initialized", pThis);
    return pThis;
}


void ircode_emitter_destroy(t_emitter_ptr pThis) {
    if (NULL == pThis)
        return;

    codemgr_destroy( pThis->ircodemgrPtr );
    tempmgr_destroy( pThis->tempmgrPtr);
    stack_clean( &(pThis->functionStack) );

    log_debug("Emitter %tx has destroyed", pThis);
    free(pThis);
}


int  ircode_emitter_run(t_emitter_ptr pThis) {
    if (NULL == pThis) {
        ERROR_REPORT_BUG("pass a NULL argument to ircode_emitter_run()");
        return -2;
    }

    if (NULL == pThis->treeRootPtr) {
        log_error( "%s: parse tree is NULL", __FUNCTION__ );
        return -1;
    }
    if (NULL == pThis->symtableRootPtr) {
        log_error( "%s: symbol table is NULL", __FUNCTION__ );
        return -1;
    }

    if ( amcc_shouldExistMain() ) {

        // Pre-check: The main() in global scope MUST be defined.
        // If main() is not defined, the emitter continues to work.

        t_symentry_ptr mainFuncEntry = symtable_find_name( pThis->symtableRootPtr, "main");
        if ( NULL == mainFuncEntry ) {
            ERROR_REPORT_INPUT(amcc_get_inputFilePath(), 0, 0, EC_NO_MAIN);
        }
        else if (false == symentry_is_func(mainFuncEntry)) {
            t_tree_ptr mainFuncNode = symentry_treenode(mainFuncEntry);
            t_position  pos = tree_get_startPosition(mainFuncNode);
            ERROR_REPORT_INPUT(pos.filePath, pos.line, pos.column, EC_BAD_MAIN);
        }
    }


    log_info( ">>> %s(): Emitting ir-codes in "
              "Syntax-Directed-Translation style ...", __FUNCTION__ );

    tree_walk ( pThis->treeRootPtr,  (t_tree_listener_ptr)pThis );

    log_info( "<<< %s(): finished to emit ir-codes with %d error%s",
              __FUNCTION__ , pThis->nErrors,
              pThis->nErrors > 1 ? "s" : "");
    return (pThis->nErrors > 0) ? -2 : 0;
}


int ircode_emitter_errors(t_emitter_ptr pThis) {
    return pThis ? pThis->nErrors : 0;
}


t_program_codes  ircode_emitter_GRAB(t_emitter_ptr pThis) {
    if (NULL == pThis || NULL == pThis->ircodemgrPtr)
        return NULL;

    t_list_ptr programCodes = pThis->ircodemgrPtr->programCodes;
    pThis->ircodemgrPtr->programCodes = NULL;
    return programCodes;
}
